/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.edit;

import java.util.Iterator;
import java.util.List;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.EList;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.uml2.uml.DestructionEvent;
import org.eclipse.uml2.uml.Event;
import org.eclipse.uml2.uml.Lifeline;
import org.eclipse.uml2.uml.OccurrenceSpecification;
import org.eclipse.uml2.uml.StateInvariant;
import org.eclipse.uml2.uml.UMLPackage;

import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.sequence.sedi.edit.policy.DeletionEditPolicy;
import com.cea.papyrus.diagram.sequence.sedi.figures.InteractionFragmentFigure1;
import com.cea.papyrus.diagram.sequence.sedi.figures.TerminateFigure;
import com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Property;

/**
 * Edit part for Stop.
 * 
 * @author Andreas Limyr
 * @author frankd@ifi.uio.no
 */
public class TerminateEditPart extends SeDiSubpartEditPart<OccurrenceSpecification> {

	/**
	 * The {@link Diagram} that contains the {@link StateInvariant}
	 */
	private Diagram diagram; 
	
    public TerminateEditPart(GraphElement view, ModelManager modelManager) {
        super(view, modelManager);

        Event event = ModelUtil.<OccurrenceSpecification> getSemantic(view).getEvent();
        if (event != null && !(event instanceof DestructionEvent)) {
            throw new IllegalArgumentException("Referenced event != DestructionEvent");
        }
        
        // Get diagram on EditPart creation instead of calling getDiagram
        // dynamically
        // Dynamic call is problematic during deletion (from Model or Diagram)
        // because the graphical container may be null.
        diagram = LookForElement.getDiagram(getGraphNode());
    }

    protected IFigure createFigure() {
        return new TerminateFigure();
    }

    @Override
    protected void createEditPolicies() {
        super.createEditPolicies();
        
        // Add edit policy to handle deletion requests (replace SeDiSubpartEditPolicy)
		installEditPolicy(
				EditPolicy.COMPONENT_ROLE,
		        new DeletionEditPolicy());
        
		// Stop doesn't need direct edit
        removeEditPolicy(EditPolicy.DIRECT_EDIT_ROLE);
    }

    // TODO Pull up to superclass
    @SuppressWarnings("unchecked")
    protected void refreshVisualsBounds() {
    	
        GraphNode model = (GraphNode) getModel();
        InteractionFragmentFigure1 figure = (InteractionFragmentFigure1) getFigure();

        final Rectangle bounds = new Rectangle(model.getPosition(), model.getSize());

        final Rectangle coveredBounds;
        List coveredList = getUmlElement().getCovereds();
        Lifeline covered = (coveredList.isEmpty() ? null : (Lifeline) coveredList.get(0));
        if (covered != null) {
            GraphNode coveredView =
                    ModelUtil.getGraphNode(covered, diagram);
            if (coveredView == null) {
            	// The covered Lifeline GraphNode is not yet created
            	// ... abort refreshVisualsBounds
            	return;
            }
            coveredBounds = new Rectangle(coveredView.getPosition(), coveredView.getSize());
        } else
            coveredBounds = null;

        final Rectangle preferred = figure.getPreferredFigureBounds(bounds, coveredBounds);

        if (getParent() != null) // may be null: when the EP is removed
            ((GraphicalEditPart) getParent()).setLayoutConstraint(this, figure, preferred);
        figure.setBounds(preferred);
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void umlModelChanged(Notification evt) {
        switch (evt.getFeatureID(UMLPackage.class)) {
        case UMLPackage.NAMED_ELEMENT:
            // ignore
            break;

        case UMLPackage.OCCURRENCE_SPECIFICATION__COVERED: {
            switch (evt.getEventType()) {
            case Notification.REMOVE:
            case Notification.REMOVE_MANY:
//                detachLifelineAdapter(evt.getOldValue());
//                break;

            case Notification.ADD:
            case Notification.ADD_MANY:
//                attachLifelineAdapter(evt.getNewValue());
//                updateBounds(evt.getNewValue());
            }
            break;
        }

        default:
            super.umlModelChanged(evt);
        }
    }

//    protected void lifelineChanged(Notification evt) {
//        switch (evt.getFeatureID(Di2Package.class)) {
//        case Di2Package.GRAPH_ELEMENT__POSITION:
//        case Di2Package.GRAPH_NODE__SIZE:
////            updateBounds(evt.getNotifier());
//        }
//    }

    @SuppressWarnings("unchecked")
    private void updateBounds(final Lifeline lifeline) {
        final GraphNode lifelineView = getLifelineView(lifeline);

        final GraphNode model = (GraphNode) getModel();
        final Rectangle currentBounds = new Rectangle(model.getPosition(), model.getSize());

        final TerminateFigure figure = (TerminateFigure) getFigure();
        final Rectangle preferredBounds =
                figure.getPreferredModelBounds(currentBounds, new Rectangle(lifelineView
                        .getPosition(), lifelineView.getSize()));

        if (!preferredBounds.equals(currentBounds)) {
            model.setPosition(preferredBounds.getLocation());
            model.setSize(preferredBounds.getSize());
        }
        // refreshVisuals();
    }

    /**
     * Retrieve covered {@link Lifeline}
     * @return the covered {@link Lifeline} or null (when the {@link Lifeline} is deleted)
     */
    private Lifeline getCoveredLifeline() {

      // Try to retrieve the covered Lifeline
      final OccurrenceSpecification termination = getUmlElement();
      Lifeline lifeline = null;
      
      EList<Lifeline> covereds = termination.getCovereds();
      if (covereds.isEmpty()) {
      	// No covered Lifeline found...
      	return null;
      }
      
      // Only the fist covered Lifeline is considered
      // This might be a problem if more than one Lifeline is covered...
      // ... is this correct considering UML specification ?
      lifeline = covereds.get(0);

      return lifeline;
    }
    
    private GraphNode getLifelineView(final Lifeline lifeline) {

        GraphNode lifelineView = null;
        if (lifeline != null) {
            GraphElement ge = (GraphElement) getModel();
            if (ge.getContainer() != null) {
                lifelineView = ModelUtil.getGraphNode(lifeline, diagram);
            }
        }

        return lifelineView;
    }

    // --- Activation ---
    @SuppressWarnings("unchecked")
    @Override
    public void activate() {
		if (this.isActive()) {
			return;
		}
		super.activate();
		// start listening for changes in the model
		((Notifier)this.getModel()).eAdapters().add(graphElementListener);
		
		// Add listener on covered Lifeline GraphNode to detect changes
		Lifeline lifeline = getCoveredLifeline();
		if (lifeline != null) {
			GraphNode lifelineGraphNode = getLifelineView(lifeline);
			if (lifelineGraphNode != null) {
				((Notifier) lifelineGraphNode).eAdapters().add(graphElementListener);
			} else {
				Debug.log(new Exception("GraphNode not found for covered lifeline."));
			}
		} else {
			Debug.log(new Exception("Covered Lifeline not found."));
		}
		
		Iterator propertiesIterator= ((GraphNode) this.getModel()).getProperty().iterator();
		while(propertiesIterator.hasNext()){
			((Notifier)propertiesIterator.next()).eAdapters().add(graphElementListener);
		}
    }

    @Override
    public void deactivate() {
		if (!this.isActive()) {
			return;
		}

		// stop listening for changes in the model
		((Notifier)this.getModel()).eAdapters().remove(graphElementListener);
		
		// Remove change listener on covered Lifeline GraphNode if the Lifeline GraphNode still exists
		Lifeline lifeline = getCoveredLifeline();
		if (lifeline != null) {
			GraphNode lifelineGraphNode = getLifelineView(lifeline);
			if (lifelineGraphNode != null) {
				((Notifier) lifelineGraphNode).eAdapters().remove(graphElementListener);
			}
		}
		
		Iterator<Property> propertiesIterator= ((GraphNode) this.getModel()).getProperty().iterator();
		while(propertiesIterator.hasNext()){
			((Notifier) propertiesIterator.next()).eAdapters().remove(graphElementListener);
		}		
		
		super.deactivate();
    }
    
//    // --- Activation ---
//
//    @SuppressWarnings("unchecked")
//    @Override
//    public void activate() {
//        super.activate();
//
//        attachLifelineAdapter(getUmlElement().getCovereds());
//    }
//
//    @Override
//    public void deactivate() {
//        detachLifelineAdapter(getUmlElement().getCovereds());
//
//        super.deactivate();
//    }
//
//    @SuppressWarnings("unchecked")
//    private void attachLifelineAdapter(final Object o) {
//        final GraphNode lifelineView = getLifelineView(o);
//        if (lifelineView != null)
//            lifelineView.eAdapters().add(lifelineAdapter);
//    }
//
//    private void detachLifelineAdapter(final Object o) {
//        final GraphNode lifelineView = getLifelineView(o);
//        if (lifelineView != null)
//            lifelineView.eAdapters().remove(lifelineAdapter);
//    }

//    protected final AdapterImpl lifelineAdapter = new AdapterImpl() {
//        public boolean isAdapterForType(final Object type) {
//            return type instanceof EObject;
//        }
//
//        public void notifyChanged(final Notification msg) {
//            if (msg.isTouch())
//                return;
//            lifelineChanged(msg);
//        }
//    };
}
